#include "decoder.hpp"
#include <QRegExp>


Decoder::Decoder(QObject *parent) :
    QObject(parent)
{
}


Decoder::~Decoder()
{
}


Command *Decoder::decodeLstString(QString line)
{
    Command *command = new Command();

    QRegExp digitReg("\\b[0-9a-fA-F]{4,5}\\b");
    QRegExp commentReg(";[\\w\\W]*");
    QString lineTrimmed = line.trimmed();

    // Check blank lines
    QRegExp blankReg("^[0-9]{5}$");
    if (blankReg.exactMatch(lineTrimmed))
    {
	command->setCommandType(Command::BLANK);
	command->setLineNumber(lineTrimmed.toInt(0, 10));

	return command;
    }

    // Check for Comment lines
    QRegExp commentLineReg("^[0-9]{5}[\\s]*;[\\w\\W]*$");
    if (commentLineReg.exactMatch(lineTrimmed))
    {
	command->setCommandType(Command::COMMENT);

	//Get Line Number
	digitReg.indexIn(lineTrimmed);
	command->setLineNumber(digitReg.capturedTexts().at(0).toInt(0, 10));

	//Get Comment
	if (commentReg.indexIn(lineTrimmed) != -1)
	{
	    QString comment = commentReg.capturedTexts().at(0);
	    command->setComment(comment.trimmed());
	}

	return command;
    }

    // Check for EQU statements
    QRegExp equReg("\\bEQU\\b");
    equReg.setCaseSensitivity(Qt::CaseInsensitive);
    if (equReg.indexIn(lineTrimmed) != -1)
    {
	command->setCommandType(Command::DEFINITION);

	//Get Line Number
	digitReg.indexIn(lineTrimmed);
	command->setLineNumber(digitReg.capturedTexts().at(0).toInt(0, 10));
	lineTrimmed.remove(digitReg);
	lineTrimmed = lineTrimmed.trimmed();

	//Get Comment
	if (commentReg.indexIn(lineTrimmed) != -1)
	{
	    QString comment = commentReg.capturedTexts().at(0);
	    comment.remove(";");
	    command->setComment(comment.trimmed());
	    lineTrimmed.remove(commentReg);
	    lineTrimmed = lineTrimmed.trimmed();
	}

	command->setAssemblerCode(lineTrimmed);

	//Get the EQU statement
	QStringList lineSplit = lineTrimmed.split("EQU", QString::SkipEmptyParts, Qt::CaseInsensitive);
	this->equMap.insert(lineSplit.at(0).trimmed(), lineSplit.at(1).trimmed().toInt(0, 16));
	command->setAnchor(lineSplit.at(0).trimmed());

	QString address = lineSplit.at(1).toUpper();
	QRegExp regExp("\\D");
	if (regExp.exactMatch(address.right(1)))
	    address.chop(1);
	if (address.size() < 2)
	    address.prepend("0");

	emit customHighlightingRule(lineSplit.at(0), 0);
	emit customTooltip(lineSplit.at(0), "Register Address: " + address + " - Value: 00");
	emit variableFound(lineSplit.at(0).trimmed(), address);

	return command;
    }


    // Get a Label
    QRegExp labelReg("^[0-9]{5}[\\s]{2}[\\S]*$");
    if (labelReg.indexIn(lineTrimmed) != -1)
    {
	command->setCommandType(Command::LABEL);

	//Get Line Number
	digitReg.indexIn(lineTrimmed);
	command->setLineNumber(digitReg.capturedTexts().at(0).toInt(0, 10));
	lineTrimmed.remove(digitReg);
	lineTrimmed = lineTrimmed.trimmed();

	//Get Comment
	if (commentReg.indexIn(lineTrimmed) != -1)
	{
	    QString comment = commentReg.capturedTexts().at(0);
	    comment.remove(";");
	    command->setComment(comment.trimmed());
	    lineTrimmed.remove(commentReg);
	    lineTrimmed = lineTrimmed.trimmed();
	}

	command->setLabel(lineTrimmed);
	this->pendingLabels.append(lineTrimmed);
	emit customHighlightingRule(lineTrimmed, 1);

	return command;
    }


    // Get a complete statement
    QRegExp stmtReg("^[0-9a-fA-F]{4}\\s[0-9a-fA-F]{4}");
    if (stmtReg.indexIn(lineTrimmed) != -1)
    {
	command->setCommandType(Command::COMMAND);

	//Get all Numbers
	int pos = 0;
	digitReg.indexIn(lineTrimmed, pos);
	command->setAdressProgramMemory(digitReg.capturedTexts().at(0).toInt(0, 16));
	pos += digitReg.matchedLength();
	digitReg.indexIn(lineTrimmed, pos);
	command->setArguments(digitReg.capturedTexts().at(0).toInt(0, 16));
	command->setOpCode(digitReg.capturedTexts().at(0).toInt(0, 16));
	pos += digitReg.matchedLength();
	digitReg.indexIn(lineTrimmed, pos);
	command->setLineNumber(digitReg.capturedTexts().at(0).toInt(0, 10));

	//Remove the numbers
	lineTrimmed.remove(digitReg);
	lineTrimmed = lineTrimmed.trimmed();

	//Get Comment
	if (commentReg.indexIn(lineTrimmed) != -1)
	{
	    QString comment = commentReg.capturedTexts().at(0);
	    comment.remove(";");
	    command->setComment(comment.trimmed());
	    lineTrimmed.remove(commentReg);
	    lineTrimmed = lineTrimmed.trimmed();
	}

	command->setAssemblerCode(lineTrimmed);

	//Check for pending Labels and assign corresponding address
	while (!pendingLabels.isEmpty())
	{
	    labelMap.insert(pendingLabels.first(), command->getAddress());
	    QString address = QString::number(command->getAddress(), 16).toUpper();
	    while (address.size() < 4)
		address.prepend("0");
	    emit customTooltip(pendingLabels.takeFirst(), "Mapped Address: " + address + "(" + command->getAssembler() + ")");
	}

	return command;
    }


    // Additional Informations found
    command->setCommandType(Command::ADDITIONAL);

    //Get Line Number
    digitReg.indexIn(lineTrimmed);
    command->setLineNumber(digitReg.capturedTexts().at(0).toInt(0, 10));
    lineTrimmed.remove(QRegExp("[0-9]{5}"));
    lineTrimmed = lineTrimmed.trimmed();

    //Get Comment
    if (commentReg.indexIn(lineTrimmed) != -1)
    {
	QString comment = commentReg.capturedTexts().at(0);
	comment.remove(";");
	command->setComment(comment.trimmed());
	lineTrimmed.remove(commentReg);
	lineTrimmed = lineTrimmed.trimmed();
    }

    command->setAssemblerCode(lineTrimmed);

    return command;
}


void Decoder::reset()
{
    this->pendingLabels.clear();
    this->labelMap.clear();
    this->equMap.clear();
}

QMap<QString, int> *Decoder::getLabelMap()
{
    return &labelMap;
}
